%{
/* -------------------------------------------------------------------------
 *
 * repl_scanner.l
 *	  a lexical scanner for the replication commands
 *
 * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
 * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/replication/repl_scanner.l
 *
 * -------------------------------------------------------------------------
 */
#include "postgres.h"
#include "knl/knl_variable.h"
#include "parser/scansup.h"

/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
#undef fprintf
#define fprintf(file, fmt, msg) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("%s", msg)));

#undef yylloc
#undef yyerror
#undef yylval

#pragma GCC diagnostic ignored "-Wsign-compare"
#pragma GCC diagnostic ignored "-Wunused-variable"
#define YYSTYPE			replication_scanner_YYSTYPE
#define yyerror(msg)	replication_scanner_yyerror(msg, yyscanner)

static void startlit(void);
static char *litbufdup(void);
static void addlit(char *ytext, int yleng);
static void addlitchar(unsigned char ychar);

/*
 * define replication_scanner_yylex for flex >= 2.6
 */
#if FLEX_MAJOR_VERSION >= 2 && FLEX_MINOR_VERSION >= 6
#define YY_DECL int replication_scanner_yylex \
               (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)
#endif

%}

%option reentrant
%option 8bit
%option bison-bridge bison-locations
%option never-interactive
%option nodefault
%option noinput
%option nounput
%option noyywrap
%option noyyalloc
%option noyyrealloc
%option noyyfree
%option warn
%option prefix="replication_scanner_yy"

%x xq xd

/* Extended quote
 * xqdouble implements embedded quote, ''''
 */
xqstart			{quote}
xqdouble		{quote}{quote}
xqinside		[^']+

/* Double quote
 * Allows embedded spaces and other special characters into identifiers.
 */
dquote			\"
xdstart			{dquote}
xdstop			{dquote}
xddouble		{dquote}{dquote}
xdinside		[^"]+

hexdigit		[0-9A-Za-z]+
integer			[0-9]+

quote			'
quotestop		{quote}

ident_start		[A-Za-z\200-\377_]
ident_cont		[A-Za-z\200-\377_0-9\$]

identifier		{ident_start}{ident_cont}*

%%

BASE_BACKUP			{ return K_BASE_BACKUP; }
FAST			{ return K_FAST; }
FETCH_MOT_CHECKPOINT	{ return K_FETCH_MOT_CHECKPOINT; }
IDENTIFY_SYSTEM		{ return K_IDENTIFY_SYSTEM; }
IDENTIFY_VERSION	{ return K_IDENTIFY_VERSION; }
IDENTIFY_MODE		{ return K_IDENTIFY_MODE; }
IDENTIFY_MAXLSN		{ return K_IDENTIFY_MAXLSN; }
IDENTIFY_CONSISTENCE	{ return K_IDENTIFY_CONSISTENCE; }
IDENTIFY_CHANNEL	{ return K_IDENTIFY_CHANNEL; }
IDENTIFY_AZ		{ return K_IDENTIFY_AZ; }
LABEL			{ return K_LABEL; }
NOWAIT			{ return K_NOWAIT; }
BUILDSTANDBY            { return K_BUILDSTANDBY; }
COPYSECUREFILE            { return K_COPYSECUREFILE; }
NEEDUPGRADEFILE         { return K_NEEDUPGRADEFILE; }
OBSMODE                 { return K_OBSMODE; }
PROGRESS			{ return K_PROGRESS; }
WAL			{ return K_WAL; }
TABLESPACE_MAP			{ return K_TABLESPACE_MAP; }
DATA		{ return K_DATA; }
START_REPLICATION	{ return K_START_REPLICATION; }
ADVANCE_REPLICATION	{ return K_ADVANCE_REPLICATION; }
CREATE_REPLICATION_SLOT		{ return K_CREATE_REPLICATION_SLOT; }
DROP_REPLICATION_SLOT		{ return K_DROP_REPLICATION_SLOT; }
PHYSICAL			{ return K_PHYSICAL; }
LOGICAL 			{ return K_LOGICAL; }
SLOT				{ return K_SLOT; }
USE_SNAPSHOT		{ return K_USE_SNAPSHOT; }
ADVANCE_CATALOG_XMIN		{ return K_ADVANCE_CATALOG_XMIN; }
WAIT				{ return K_WAIT; }

","				{ return ','; }
";"				{ return ';'; }
"("				{ return '('; }
")"				{ return ')'; }

[\n]			;
[\t]			;
" "				;

{hexdigit}+\/{hexdigit}+		{
					uint32 hi,
								lo;
					if (sscanf(yytext, "%X/%X", &hi, &lo) != 2)
						yyerror("invalid streaming start location");
					yylval->recptr = (((uint64) hi) << 32) | lo;
					return RECPTR;
				}

{integer}			{
					long val;
					char *endptr;
					val = strtol(yytext, &endptr, 10);
					if (*endptr != '\0' || errno == ERANGE)
						yyerror("integer too large");
					yylval->ival = val;
					return ICONST;
				}

{xqstart}		{
					BEGIN(xq);
					startlit();
				}
				
<xq>{quotestop}	{
					yyless(1);
					BEGIN(INITIAL);
					yylval->str = litbufdup();
					return SCONST;
				}
<xq>{xqdouble} {
					addlitchar('\'');
				}
<xq>{xqinside}  {
					addlit(yytext, yyleng);
				}

{xdstart}		{
					BEGIN(xd);
					startlit();
				}

<xd>{xdstop}	{
					int len;
					yyless(1);
					BEGIN(INITIAL);
					yylval->str = litbufdup();
					len = strlen(yylval->str);
					truncate_identifier(yylval->str, len, true);
					return IDENT;
				}

<xd>{xdinside}  {
					addlit(yytext, yyleng);
				}

{identifier}	{
					int len = strlen(yytext);

					yylval->str = downcase_truncate_identifier(yytext, len, true);
					return IDENT;
				}

<xq,xd><<EOF>>	{ yyerror("unterminated quoted string"); }


<<EOF>>			{
					yyterminate();
				}

.				{
					return T_WORD;
				}
%%


static void
startlit(void)
{
	initStringInfo(t_thrd.replscanner_cxt.litbuf);
}

static char *
litbufdup(void)
{
	return t_thrd.replscanner_cxt.litbuf->data;
}

static void
addlit(char *ytext, int yleng)
{
	appendBinaryStringInfo(t_thrd.replscanner_cxt.litbuf, ytext, yleng);
}

static void
addlitchar(unsigned char ychar)
{
	appendStringInfoChar(t_thrd.replscanner_cxt.litbuf, ychar);
}

void
replication_scanner_yyerror(const char *message, replication_scanner_yyscan_t yyscanner)
{
	/* report only the first error in a parse operation */
	return;
}

replication_scanner_yyscan_t
replication_scanner_init(const char *str)
{
	Size		slen = strlen(str);
	char	   *scanbuf;
	yyscan_t	scanner;
	errno_t     rc;

	if (yylex_init(&scanner) != 0) {
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
							 errmsg("yyscanner init failed: %m")));
	}

	/*
	 * Make a scan buffer with special termination needed by flex.
	 */
	scanbuf = (char *) palloc(slen + 2);
	rc = memcpy_s(scanbuf, slen, str, slen);
	securec_check(rc, "\0", "\0");

	scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
	t_thrd.replscanner_cxt.scanbufhandle = yy_scan_buffer(scanbuf, slen + 2, scanner);

	return scanner;
}

void
replication_scanner_finish(replication_scanner_yyscan_t yyscanner)
{
	yy_delete_buffer(t_thrd.replscanner_cxt.scanbufhandle, yyscanner);
	t_thrd.replscanner_cxt.scanbufhandle = NULL;
	yylex_destroy(yyscanner);
}

void *
replication_scanner_yyalloc(yy_size_t bytes, replication_scanner_yyscan_t yyscanner)
{
    return palloc(bytes);
}

void *
replication_scanner_yyrealloc(void *ptr, yy_size_t bytes, replication_scanner_yyscan_t yyscanner)
{
    if (ptr)
        return repalloc(ptr, bytes);
    else
        return palloc(bytes);
}

void
replication_scanner_yyfree(void *ptr, replication_scanner_yyscan_t yyscanner)
{
    if (ptr)
	{
        pfree(ptr);
		ptr = NULL;
	}
}
